class ValueObject:
    def __init__(self, source: str):
        self.string = source
        self.binCode = b''
        self.address, self.size = 0, 0

    def dispatch(self, name: str, arg_list: dict, count: int = -1):
        pass

    def translate(self, arg_list: dict):
        pass

    def encode(self, arg_list: dict):
        pass

    def to_byte(self):
        pass


class Command(ValueObject):
    def __init__(self, source: str):
        ValueObject.__init__(self, source)
        self.type, self.size = 'Command', 4
        self.opcode, self.oparg = None, None

    def dispatch(self, name: str, arg_list: dict, count: int):
        if self.string.split(' ')[0] == 'LABEL':
            self.opcode = 'None'
            arg_list[name+'::'+self.string.split(' ')[1]] = count
            return
        self.opcode = self.string.split(' ')[0]
        if len(self.string.split(' ')) > 1:
            self.oparg = self.string.split(' ')[1]
            try:
                self.oparg = self.oparg.split(',')
            except:
                pass

    def encode(self, arg_list: dict, command_list: dict):
        self.opcode = command_list[self.opcode]
        if self.oparg == None:
            return
        for i in range(len(self.oparg)):
            if self.oparg[i] in arg_list:
                self.oparg[i] = arg_list[self.oparg[i]]
            elif self.oparg[i].startswith('0x'):
                self.oparg[i] = int(self.oparg[i][2:], 16)
            elif self.oparg[i].startswith('ARG'):
                self.oparg[i] = 16+int(self.oparg[i][3:])
            elif self.oparg[i].startswith('RET'):
                self.oparg[i] = 32+int(self.oparg[i][3:])
            elif self.oparg[i].startswith('TEMP'):
                self.oparg[i] = 48+int(self.oparg[i][3:])

    def to_byte(self):
        self.opcode <<= 24
        if self.oparg == None:
            self.binCode = self.opcode.to_bytes(4, 'little')
            return
        match ((self.opcode >> 24)//32):
            case 0:
                self.oparg[0] <<= 16
            case 1:
                for i in range(len(self.oparg)):
                    self.oparg[i] <<= 8*(2-i)
            case 2:
                self.oparg[0] <<= 16
            case 3 | 4:
                pass
        self.opcode += sum(self.oparg)
        self.binCode = self.opcode.to_bytes(4, 'little')


class Data(ValueObject):
    def __init__(self, source: str):
        ValueObject.__init__(self, source)
        self.type = 'Data'
        self.name, self.value = None, None

    def dispatch(self, name: str, arg_list: dict, count: int = -1):
        self.name = self.string.split(' ')[0]
        self.value = [arg_list[self.string.split(
            ' ')[1]], self.string.split(' ')[2]]
        arg_list[name+'::'+self.name] = 'None'

    def translate(self, arg_list: dict):
        if self.value[0] == -1:
            self.size = len(self.value[1][1:-1])
            self.value[1] = bytes(self.value[1][1:-1], 'utf-8')
        else:
            self.size = self.value[0]
            if self.value[1].startswith('0x'):
                self.value[1] = int(self.value[1][2:], 16)
            else:
                self.value[1] = int(self.value[1])

    def to_byte(self):
        if self.value[0] == -1:
            self.binCode = bytes(self.value[1], 'utf-8')
        else:
            self.binCode = self.value[1].to_bytes(self.size, 'little')


class Parament:
    def __init__(self, name: str, type: int) -> None:
        self.name, self.type = name, type
        self.size, self.entry = 0, 0
        self.datas, self.bytes = [], b''

    def Encode(self, arg_list: dict, command_list: dict):
        if self.type == 1:
            return
        for i in self.datas:
            i.encode(arg_list, command_list)

    def setEntry(self):
        if self.datas[0].split(' ')[0] == 'ORG':
            self.entry = int(self.datas[0].split(' ')[1][2:], 16)
            self.datas.pop(0)
            self.size -= 1

    def setAddress(self):
        for i in range(len(self.datas)):
            if i == 0:
                self.datas[i].address = self.entry
            else:
                self.datas[i].address = self.datas[i -
                                                   1].address+self.datas[i-1].size

    def putAddress(self, arg_list: dict):
        if self.type == 0:
            for i in arg_list.keys():
                if i.find(self.name+'::') != -1:
                    arg_list[self.name+'::'+i.name] = self.datas[i].address
            return
        for i in self.datas:
            if (self.name+'::'+i.name) in arg_list.keys():
                arg_list[self.name+'::'+i.name] = i.address

    def to_bytes(self, fentry: int) -> int:
        if self.type == 0:
            self.size = ((4*self.size)//16+1) * \
                16 if (4*self.size) % 16 != 0 else 4*self.size
        else:
            self.size = self.datas[-1].size+self.datas[-1].address-self.entry
            self.size = (self.size//16+1) * \
                16 if self.size % 16 != 0 else self.size
        self.bytes = self.entry.to_bytes(2, 'little')
        self.bytes += self.size.to_bytes(2, 'little')
        self.bytes += fentry.to_bytes(2, 'little')
        self.bytes += self.type.to_bytes(1, 'little')+b'\x00'
        for i in self.datas:
            i.to_byte()
        return self.size+fentry
